package mn.idax.exchange.otc.service.impl;

import com.alibaba.fastjson.JSON;
import mn.idax.exchange.otc.constant.ReferenceExchageConstant;
import mn.idax.exchange.otc.domain.ConnectionDTO;
import mn.idax.exchange.otc.entity.JPushMessage;
import mn.idax.exchange.otc.entity.OTCPayModeDetail;
import mn.idax.exchange.otc.entity.Systemsetting;
import mn.idax.exchange.otc.mapper.SystemsettingMapper;
import mn.idax.exchange.otc.service.*;
import mn.idax.exchange.otc.util.CacheUtil;
import mn.idax.exchange.otc.util.LogUtils;
import mn.idax.exchange.otc.vo.ADRecommendListResult;
import mn.idax.exchange.otc.vo.OTCAdPageSecDTO;
import mn.idax.exchange.otc.vo.PayTypeDTO;
import mn.idax.exchange.otc.vo.ProportionDTO;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * Create by jack_huang on 2018/12/10
 *
 * @author jack_huang
 */
@Service
@Order(value = 1)
public class MemoryService implements CommandLineRunner {
    @Value("${config.keyPath}")
    private String keyPath;
    @Value("${config.userKeyPath}")
    private String userKeyPath;


    public static final ConcurrentHashMap MARKETMAP = new ConcurrentHashMap();
    /**
     * 支付方式对应国际化
     **/
    public static final ConcurrentHashMap PAYTYPEMAP = new ConcurrentHashMap();
    /**
     * 支付方式图标
     **/
    public static final ConcurrentHashMap PAYTYPEICONMAP = new ConcurrentHashMap();
    /**
     * 系统表信息
     */
    public static final ConcurrentHashMap SYSTEMSETTINGMAP = new ConcurrentHashMap();
    /**
     * 固定价格关闭显示的国家
     */
    public static final ConcurrentHashMap CLOSEDFIXEDSWITCH = new ConcurrentHashMap();
    /**
     * 所有国家的固定价格开关
     */
    public static final ConcurrentHashMap FIXEDSWITCH = new ConcurrentHashMap();
    /**
     * 不同国家的浮动价格
     */
    public static final ConcurrentHashMap FLOATINGRATE = new ConcurrentHashMap();
    //jpushlog表数据
    public static final ConcurrentHashMap<String, JPushMessage> JPUSHTEMPLATE = new ConcurrentHashMap();
    /**
     * AD_LIST数据
     */
    public static final ConcurrentHashMap<String, List<ADRecommendListResult>> AD_LIST = new ConcurrentHashMap();

    /**
     * 广告主的加权系数
     */
    public static final ConcurrentHashMap<Integer, ProportionDTO> AD_PUBLISER_PROPORTION_MAP = new ConcurrentHashMap();



    @Autowired
    private RedisService redisService;
    @Autowired
    private SystemsettingMapper systemsettingMapper;
    @Autowired
    private JPushMessageService jPushMessageService;
    @Autowired
    private OTCSysDictionaryItemService otcSysDictionaryItemService;
    @Autowired
    private OTCPayModeDetailService otcPayModeDetailService;
    @Autowired
    private OTCAdsService otcAdsService;


    /**
    /**
     * TradeSuccess,PairName,Qty,Buyer,Seller
     *
     * 根据人，写个方法去算 集合中过滤这个人（区分买/卖）的所有交易信息，区分交易币种 算好后丢到map里面保存
     * 广告列表算法时 每次去map里面拿
     * 上架 广告时 看下map里面是否有这个人的聚合
     * 下架时看下这个人是否只有一条广告 是就移除 else no action
     * 开定时任务 每天算一次
     */
    @PostConstruct
    public void initPublisherProportion(){


        Map mkMap = redisService.getHashValue(ReferenceExchageConstant.HashMarket);
        if (mkMap != null) {
            MARKETMAP.putAll(mkMap);
        }

        otcAdsService.getUserProportion();

    }




    @Override
    public void run(String... strings) {

        long initialDelay1 = 1;
        long period1 = 1;

        // 公钥
        initKey();
        initUserEncryptKey();


        ScheduledExecutorService marketService = Executors.newScheduledThreadPool(1);
        marketService.scheduleAtFixedRate(new Thread("syncRedisMarketCacheThread") {
            @Override
            public void run() {
                try {
                    //实时成交价
                    Map mkMap = redisService.getHashValue(ReferenceExchageConstant.HashMarket);
                    if (mkMap != null) {
                        MARKETMAP.putAll(mkMap);
                    }

                } catch (Exception e) {
                    LogUtils.error(MemoryService.class, "initLoadHashMarket to memory syncRedisMarketCacheThread exception ********************: " + ExceptionUtils.getStackTrace(e));
                }
            }
        }, initialDelay1, period1, TimeUnit.MINUTES);

        /**
         * 定时获取支付方式
         */
        long initialDelay2 = 0;
        long period2 = 3;

        ScheduledExecutorService payTypeService = Executors.newScheduledThreadPool(1);
        payTypeService.scheduleAtFixedRate(new Thread("syncPayTypeMapThread") {
            @Override
            public void run() {
                try {
                    //支持的支付方式
                    List<PayTypeDTO> payTypes = otcSysDictionaryItemService.getPayType();

                    if (!payTypes.isEmpty()) {
                        for (PayTypeDTO payType : payTypes) {
                            PAYTYPEMAP.put(payType.getCode(), payType.getPayType());
                            PAYTYPEICONMAP.put(payType.getCode(), payType.getPayTypeIcon());

                        }
                    }

                } catch (Exception e) {
                    LogUtils.error(MemoryService.class, "initLoadPayTypeMap to memory syncPayTypeMapThread exception ********************: " + ExceptionUtils.getStackTrace(e));
                }
            }
        }, initialDelay2, period2, TimeUnit.MINUTES);

        /**
         * 定时获取系统表信息
         */
        long initialDelay3 = 0;
        long period3 = 1;

        ScheduledExecutorService systemSetting = Executors.newScheduledThreadPool(1);
        systemSetting.scheduleAtFixedRate(new Thread("syncSystemSettingThread") {
            @Override
            public void run() {
                try {
                    List<Systemsetting> allSystemsetting = systemsettingMapper.getAllSystemsetting();

                    if(!allSystemsetting.isEmpty()){
                        SYSTEMSETTINGMAP.putAll(allSystemsetting.stream().collect(Collectors.toMap(Systemsetting::getKey, systemsetting -> systemsetting.getValue())));
                    }
                } catch (Exception e) {
                    LogUtils.error(MemoryService.class, "initLoadSystemSetting to memory syncSystemSettingThread exception ********************: " + ExceptionUtils.getStackTrace(e));
                }
            }
        }, initialDelay3, period3, TimeUnit.MINUTES);

        /**
         * 获取固定价格关闭的国家
         */
        long initialDelay4 = 0;
        long period4 = 2;

        ScheduledExecutorService closedFixedSwitch = Executors.newScheduledThreadPool(1);
        closedFixedSwitch.scheduleAtFixedRate(new Thread("closedFixedSwitchThread") {
            @Override
            public void run() {
                try {
                    //获取固定价格关闭的国家
                    List<OTCPayModeDetail> closedFixedSwitchList = otcPayModeDetailService.getOTCPayModeDetail();

                    if (!closedFixedSwitchList.isEmpty()) {
                        for (OTCPayModeDetail otcPayModeDetail : closedFixedSwitchList) {
                            FLOATINGRATE.put(otcPayModeDetail.getCountryid(), otcPayModeDetail.getFloatingrate());
                            FIXEDSWITCH.put(otcPayModeDetail.getCountryid(), otcPayModeDetail.getFixedswitch());
                            if (!otcPayModeDetail.getFixedswitch()) {
                                CLOSEDFIXEDSWITCH.put(otcPayModeDetail.getCountryid(), otcPayModeDetail.getFixedswitch());
                            }

                        }
                    }
                } catch (Exception e) {
                    LogUtils.error(MemoryService.class, "initLoadClosedFixedSwitchList to memory closedFixedSwitch exception ********************: " + ExceptionUtils.getStackTrace(e));
                }
            }
        }, initialDelay4, period4, TimeUnit.MINUTES);


        //获取jpush模板
        ScheduledExecutorService jPushTemplate = Executors.newScheduledThreadPool(1);
        jPushTemplate.scheduleAtFixedRate(new Thread("syncSystemSettingThread") {
            @Override
            public void run() {
                List<JPushMessage> jPushedMessages = jPushMessageService.getJPushedMessages();
                if (!jPushedMessages.isEmpty()) {
                    for (JPushMessage jPushedMessage : jPushedMessages) {
                        JPUSHTEMPLATE.put(jPushedMessage.getBusiness().toString().concat(jPushedMessage.getLanguage()), jPushedMessage);
                    }
                }
                LogUtils.info(this.getClass(), "jpushMessage data is {}" + JSON.toJSONString(JPUSHTEMPLATE));
            }
        }, 0, 3, TimeUnit.MINUTES);


        //移除长时间未传token的连接
        ScheduledExecutorService connectionsService = Executors.newScheduledThreadPool(1);
        connectionsService.scheduleAtFixedRate(new Thread("syncSystemSettingThread") {
            @Override
            public void run() {

                Collection<ConnectionDTO> connections = MessageNotifyService.CONNECTIONS.values();
                if (!CollectionUtils.isEmpty(connections)) {
                    long current = System.currentTimeMillis();

                    for (ConnectionDTO value : connections) {
                        if (value.getTimestamp() != 0 && current - value.getTimestamp() > 5) {
                            try {
                                value.getResource().close();
                            } catch (IOException e) {
                                LogUtils.info(MemoryService.class, "connectionsService fail," + ExceptionUtils.getStackTrace(e));
                            }
                        }

                    }
                }
            }
        }, 0, 5, TimeUnit.SECONDS);


        //首页广告分页
        ScheduledExecutorService otcadListService = Executors.newScheduledThreadPool(1);
        otcadListService.scheduleAtFixedRate(new Thread("syncOTCAdListThread") {
            @Override
            public void run() {
//                if (CollectionUtils.isEmpty(MemoryService.AD_PUBLISER_PROPORTION_MAP)){
//                    return;
//                }

                OTCAdPageSecDTO otcAdPageSecDTO = new OTCAdPageSecDTO();
                try {
                    List<ADRecommendListResult> adList = otcAdsService.getADPageSec(otcAdPageSecDTO);

                    /**
                     * 作过滤
                     * 同一个商户只能出现一条广告（只显示发布时间是最近的广告） 并且同一个币种、同一个方向（购买或出售）、同一个价格（浮动价格：同一交易所、同一浮动率）
                     */
                    if (!adList.isEmpty()){
                        adList = adList.stream().collect(
                                Collectors. collectingAndThen(
                                        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getUserId()
                                                + ";" + o.getType()
                                                + ";" + o.getPrice()
                                        ))), ArrayList::new)
                        );
                    }
                    AD_LIST.put("adList", adList);

                } catch (Exception e) {
                    LogUtils.info(MemoryService.class, "otcadListServiceFail :" + ExceptionUtils.getStackTrace(e));
                }
            }
        }, 0, 10, TimeUnit.SECONDS);

    }

    private void initKey() {
        InputStream stream = null;

        try {

            if(keyPath==null || keyPath.length()<1) {
                stream=this.getClass().getClassLoader().getResourceAsStream("private_key.pem");
                CacheUtil.privateKey = IOUtils.toString(stream,Charset.defaultCharset());

                stream=this.getClass().getClassLoader().getResourceAsStream("public_key.pem");
                CacheUtil.publicKeyRaw=IOUtils.toString(stream,Charset.defaultCharset());
//
//                stream=this.getClass().getClassLoader().getResourceAsStream("base.cer");
//                CacheUtil.cert=IOUtils.toString(stream,Charset.defaultCharset());
            }else {
                File file = new File(keyPath + "/private_key.pem");
                if(!file.exists()) {
                    throw new Exception("private_key.pem not exist");
                }

                stream=new FileInputStream(file);
                CacheUtil.privateKey = IOUtils.toString(stream,Charset.defaultCharset());


                file = new File(keyPath + "/public_key.pem");
                if(!file.exists()) {
                    throw new Exception("public_key.pem not exist");
                }

                stream=new FileInputStream(file);
                CacheUtil.publicKeyRaw=IOUtils.toString(stream,Charset.defaultCharset());

//
//                file = new File(config.getKeyPath() + "/base.cer");
//                if(!file.exists()) {
//                    throw new Exception("base.cer not exist");
//                }
//
//                stream=new FileInputStream(file);
//                CacheUtil.cert=IOUtils.toString(stream,Charset.defaultCharset());
            }

            CacheUtil.publicKeyRaw = CacheUtil.publicKeyRaw.replace("\r\n", "\n");

//            CacheUtil.cert = CacheUtil.cert.replace("\r\n", "\n");
//
//            Map<String,String> map = (Map<String,String>)JSON.parse(config.getDomains());
//            map.put("android-cert", CacheUtil.cert);
//            config.setDomains(JSON.toJSONString(map));

            CacheUtil.privateKey = CacheUtil.privateKey.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replace("\r", "").replace("\n", "");
            CacheUtil.publicKey=CacheUtil.publicKeyRaw.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "").replace("\r", "").replace("\n", "");


        }catch(Exception ex) {
            LogUtils.error(this.getClass(),"{}", ex);
        }
    }
    private void initUserEncryptKey() {
        InputStream stream = null;
        try {

            if(userKeyPath==null || userKeyPath.length()<1) {
                stream=this.getClass().getClassLoader().getResourceAsStream("PrivateKey.pem");
                CacheUtil.userPrivateKey = IOUtils.toString(stream, Charset.defaultCharset());
                stream=this.getClass().getClassLoader().getResourceAsStream("PublicKey.pem");
                CacheUtil.userPublicKeyRaw = IOUtils.toString(stream, Charset.defaultCharset());
            }else {
                File file = new File(userKeyPath + "/PrivateKey.pem");
                if (!file.exists()) {
                    throw new Exception("PrivateKey.pem not exist");
                }
                stream = new FileInputStream(file);
                CacheUtil.userPrivateKey = IOUtils.toString(stream, Charset.defaultCharset());

                stream=this.getClass().getClassLoader().getResourceAsStream("PublicKey.pem");
                CacheUtil.userPublicKeyRaw = IOUtils.toString(stream, Charset.defaultCharset());

                file = new File(userKeyPath + "/PublicKey.pem");
                if (!file.exists()) {
                    throw new Exception("PublicKey.pem not exist");
                }

            }
        }catch(Exception ex) {
            LogUtils.error(this.getClass(),"{}", ex);
        }

        CacheUtil.userPublicKeyRaw  = CacheUtil.userPublicKeyRaw .replace("\r\n", "\n");

        CacheUtil.userPrivateKey = CacheUtil.userPrivateKey.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replace("\r", "").replace("\n", "");
        CacheUtil.userPublicKey = CacheUtil.userPublicKeyRaw.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "").replace("\r", "").replace("\n", "");
        redisService.setString("USER:ENCRYPT:PRIVATE:KEY" , CacheUtil.userPrivateKey);

    }
}
